package com.zhhtech.zhh.api.core.adapter;

import com.alibaba.fastjson.JSON;
import com.zhhtech.zhh.api.core.annotation.RequestConfig;
import com.squareup.moshi.JsonAdapter;
import com.zhhtech.zhh.api.core.ApiHelperKt;
import com.zhhtech.zhh.api.core.FirmException;
import com.zhhtech.zhh.api.core.base.IResult;
import com.zhhtech.zhh.api.core.FirmException;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.Objects;
import java.util.concurrent.Executor;

import okhttp3.Request;
import okio.Timeout;
import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;

public class CoreCallAdapterFactory extends CallAdapter.Factory {

    public static CoreCallAdapterFactory create() {
        return create(null);
    }

    public static CoreCallAdapterFactory create(Class<? extends IResult> resultClz) {
        return new CoreCallAdapterFactory(resultClz);
    }

    private Class<? extends IResult> resultClz;

    public CoreCallAdapterFactory(Class<? extends IResult> resultClz) {
        this.resultClz = resultClz;
    }

    @Override
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) != Call.class) {
            return null;
        }
        return new CoreCallAdapter(resultClz, retrofit.callbackExecutor(), annotations, returnType);
    }

    static class CoreCallAdapter implements CallAdapter<Object, Call<?>> {

        private final Executor callbackExecutor;

        private CoreCallBuilder mCoreCallBuilder;

        public CoreCallAdapter(Class<? extends IResult> defaultResultClz,
                               Executor executor,
                               Annotation[] annotations,
                               Type returnType) {
            this.callbackExecutor = executor;
            returnType = getParameterUpperBound(0, (ParameterizedType) returnType);
            mCoreCallBuilder = CoreCallBuilder.builder()
                    .defaultResultClz(defaultResultClz)
                    .checkResultCode(true)
                    .returnType(returnType)
                    .responseType(returnType);
            RequestConfig requestConfig = findAnnotation(annotations, RequestConfig.class);
            if (requestConfig != null) {
                mCoreCallBuilder.retryCount(requestConfig.retryCount());
                mCoreCallBuilder.checkResultCode(requestConfig.checkResultCode());
                mCoreCallBuilder.retryInterval(requestConfig.unit().toMillis(requestConfig.retryInterval()));
                Class<? extends IResult> resultClz = requestConfig.resultClz();
                // 不是不处理实现以及返回值跟配置的返回结果不一样则使用配置的返回结果做json解析
                if (resultClz != IResult.class && getRawType(returnType) != requestConfig.resultClz()) {
                    mCoreCallBuilder.responseType(resultClz);
                    mCoreCallBuilder.resultGetData(requestConfig.resultGetData());
                }
            } else {
                if (defaultResultClz != null) {
                    // 默认全部用指定结果接收器封装
                    mCoreCallBuilder.responseType(defaultResultClz);
                    mCoreCallBuilder.resultGetData(true);
                    mCoreCallBuilder.checkResultCode(true);
                }
            }
        }

        @Override
        public Type responseType() {
            return mCoreCallBuilder.responseType;
        }

        @Override
        public Call<?> adapt(Call<Object> call) {
            return new ExecutorCallbackCall(mCoreCallBuilder, callbackExecutor, call);
        }
    }

    static class CoreCallBuilder {
        /**
         * 默认结果包装类
         */
        private Class<? extends IResult> defaultResultClz;
        /**
         * 本来的返回类型
         */
        private Type returnType;
        /**
         * 请求返回类型
         */
        private Type responseType;
        /**
         * 错误重试次数
         */
        private int retryCount = RequestConfig.DEFAULT_RETRY_COUNT;
        /**
         * 重试间隔
         */
        private long retryInterval = 0;
        /**
         * 是否取出data
         */
        private boolean resultGetData = false;
        /**
         * 是否校验返回code
         */
        private boolean checkResultCode = false;

        public static CoreCallBuilder builder() {
            return new CoreCallBuilder();
        }

        public CoreCallBuilder defaultResultClz(Class<? extends IResult> defaultResultClz) {
            this.defaultResultClz = defaultResultClz;
            return this;
        }

        public CoreCallBuilder returnType(Type returnType) {
            this.returnType = returnType;
            return this;
        }

        public CoreCallBuilder responseType(Type responseType) {
            this.responseType = responseType;
            return this;
        }

        public CoreCallBuilder retryCount(int retryCount) {
            this.retryCount = retryCount;
            return this;
        }

        public CoreCallBuilder retryInterval(long retryInterval) {
            this.retryInterval = retryInterval;
            return this;
        }

        public CoreCallBuilder resultGetData(boolean resultGetData) {
            this.resultGetData = resultGetData;
            return this;
        }

        public CoreCallBuilder checkResultCode(boolean checkResultCode) {
            this.checkResultCode = checkResultCode;
            return this;
        }
    }

    static final class ExecutorCallbackCall implements Call<Object> {
        final Executor callbackExecutor;
        final Call<Object> delegate;
        private CoreCallBuilder coreCallBuilder;

        ExecutorCallbackCall(CoreCallBuilder coreCallBuilder, Executor callbackExecutor, Call<Object> delegate) {
            this.coreCallBuilder = coreCallBuilder;
            this.callbackExecutor = callbackExecutor;
            this.delegate = delegate;
        }

        @Override
        public void enqueue(final Callback<Object> callback) {
            Objects.requireNonNull(callback, "callback == null");
            execute(callback);
        }

        private void execute(Callback<Object> callback) {
            delegate.enqueue(
                    new Callback<Object>() {
                        @Override
                        public void onResponse(Call<Object> call, final Response<Object> response) {
                            callbackExecutor.execute(
                                    () -> {
                                        if (delegate.isCanceled()) {
                                            callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                                        } else {
                                            Object body = response.body();
                                            if (body instanceof IResult) {
                                                // 判断code并且抛异常
                                                IResult result = (IResult) body;
                                                if (coreCallBuilder.checkResultCode && !result.isOk()) {
                                                    onFailure(call, new FirmException(result.getErrorMsg(), result.getCode(), null));
                                                    return;
                                                }
                                                // 返回值直接就是具体泛型的情况直接取出并且重新解析
                                                if (coreCallBuilder.resultGetData) {
                                                    body = result.getTData();
                                                    if (body != null) {
                                                        try {
                                                            JsonAdapter<?> adapter = ApiHelperKt.getMOSHI().adapter(coreCallBuilder.returnType);
                                                            if (body instanceof String) {
                                                                body = adapter.fromJson(body.toString());
                                                            } else {
                                                                body = adapter.fromJson(JSON.toJSONString(body));
                                                            }
                                                        } catch (Exception e) {
                                                            e.printStackTrace();
                                                            onFailure(call, e);
                                                            return;
                                                        }
                                                    }
                                                }
                                                callback.onResponse(ExecutorCallbackCall.this, Response.success(body));
                                            } else {
                                                callback.onResponse(ExecutorCallbackCall.this, response);
                                            }
                                        }
                                    });
                        }

                        @Override
                        public void onFailure(Call<Object> call, final Throwable t) {
                            callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
                        }
                    });
        }

        @Override
        public boolean isExecuted() {
            return delegate.isExecuted();
        }

        @Override
        public Response<Object> execute() throws IOException {
            return delegate.execute();
        }

        @Override
        public void cancel() {
            delegate.cancel();
        }

        @Override
        public boolean isCanceled() {
            return delegate.isCanceled();
        }

        @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
        @Override
        public Call<Object> clone() {
            return new ExecutorCallbackCall(coreCallBuilder, callbackExecutor, delegate.clone());
        }

        @Override
        public Request request() {
            return delegate.request();
        }

        @Override
        public Timeout timeout() {
            return delegate.timeout();
        }
    }

    /**
     * 寻找指定注解
     *
     * @param annotations 注解集合
     * @param cls         需要虚招打注解
     */
    static <T> T findAnnotation(Annotation[] annotations, Class<? extends Annotation> cls) {
        for (Annotation annotation : annotations) {
            if (cls.isInstance(annotation)) {
                return (T) annotation;
            }
        }
        return null;
    }

    public static Type getParameterUpperBound(int index, ParameterizedType type) {
        Type[] types = type.getActualTypeArguments();
        if (index < 0 || index >= types.length) {
            throw new IllegalArgumentException(
                    "Index " + index + " not in range [0," + types.length + ") for " + type);
        }
        Type paramType = types[index];
        if (paramType instanceof WildcardType) {
            return ((WildcardType) paramType).getUpperBounds()[0];
        }
        return paramType;
    }
}
